package com.yangtu.nearbyshop.merc.service;

import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.pagehelper.PageInfo;
import com.yangtu.nearbyshop.core.express.dao.ExpressInfo;
import com.yangtu.nearbyshop.core.notify.NotifyService;
import com.yangtu.nearbyshop.core.notify.NotifyType;
import com.yangtu.nearbyshop.core.qcode.QCodeService;
import com.yangtu.nearbyshop.core.system.SystemConfig;
import com.yangtu.nearbyshop.core.util.DateTimeUtil;
import com.yangtu.nearbyshop.core.util.JacksonUtil;
import com.yangtu.nearbyshop.core.util.ResponseUtil;
import com.yangtu.nearbyshop.db.domain.*;
import com.yangtu.nearbyshop.db.service.*;
import com.yangtu.nearbyshop.db.service.itf.INearbyshopMercRebateService;
import com.yangtu.nearbyshop.db.util.CouponUserConstant;
import com.yangtu.nearbyshop.db.util.OrderHandleOption;
import com.yangtu.nearbyshop.db.util.OrderUtil;
import com.yangtu.nearbyshop.merc.util.IpUtil;
import com.yangtu.nearbyshop.merc.util.WxResponseCode;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.yangtu.nearbyshop.core.express.ExpressService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 订单服务
 *
 * <p>
 * 订单状态：
 * 101 订单生成，未支付；102，下单后未支付用户取消；103，下单后未支付超时系统自动取消
 * 201 支付完成，商家未发货；202，订单生产，已付款未发货，但是退款取消；
 * 301 商家发货，用户未确认；
 * 401 用户确认收货； 402 用户没有确认收货超过一定时间，系统自动确认收货；
 *
 * <p>
 * 用户操作：
 * 当101用户未付款时，此时用户可以进行的操作是取消订单，或者付款操作
 * 当201支付完成而商家未发货时，此时用户可以取消订单并申请退款
 * 当301商家已发货时，此时用户可以有确认收货的操作
 * 当401用户确认收货以后，此时用户可以进行的操作是删除订单，评价商品，或者再次购买
 * 当402系统自动确认收货以后，此时用户可以删除订单，评价商品，或者再次购买
 *
 * <p>
 * 注意：目前不支持订单退货和售后服务
 */
@Service
public class MercOrderService {
    private final Log logger = LogFactory.getLog(MercOrderService.class);

    @Autowired
    private NearbyshopUserService userService;
    @Autowired
    private NearbyshopOrderService orderService;
    @Autowired
    private NearbyshopOrderGoodsService orderGoodsService;
    @Autowired
    private NearbyshopAddressService addressService;
    @Autowired
    private NearbyshopCartService cartService;
    @Autowired
    private NearbyshopRegionService regionService;
    @Autowired
    private NearbyshopGoodsProductService productService;
    @Autowired
    private WxPayService wxPayService;
    @Autowired
    private NotifyService notifyService;
    @Autowired
    private NearbyshopUserFormIdService formIdService;
    @Autowired
    private NearbyshopGrouponRulesService grouponRulesService;
    @Autowired
    private NearbyshopGrouponService grouponService;
    @Autowired
    private QCodeService qCodeService;
    @Autowired
    private ExpressService expressService;
    @Autowired
    private NearbyshopCommentService commentService;
    @Autowired
    private NearbyshopCouponService couponService;
    @Autowired
    private NearbyshopCouponUserService couponUserService;
    @Autowired
    private CouponVerifyService couponVerifyService;
    @Autowired
    private NearbyshopGoodsService goodsService;

    @Autowired
    private NearbyshopMercService mercService;

    @Autowired
    private INearbyshopMercRebateService rebateService;

    private String detailedAddress(NearbyshopAddress nearbyshopAddress) {
        Integer provinceId = nearbyshopAddress.getProvinceId();
        Integer cityId = nearbyshopAddress.getCityId();
        Integer areaId = nearbyshopAddress.getAreaId();
        String provinceName = regionService.findById(provinceId).getName();
        String cityName = regionService.findById(cityId).getName();
        String areaName = regionService.findById(areaId).getName();
        String fullRegion = provinceName + " " + cityName + " " + areaName;
        return fullRegion + " " + nearbyshopAddress.getAddress();
    }

    /**
     * 订单详情
     *
     * @param mercId  用户ID
     * @param orderId 订单ID
     * @return 订单详情
     */
    public Object detail(Integer mercId, Integer orderId) {
        if (mercId == null) {
            return ResponseUtil.unlogin();
        }

        // 订单信息
        NearbyshopOrder order = orderService.findById(orderId);
        if (null == order) {
            return ResponseUtil.fail(WxResponseCode.ORDER_UNKNOWN, "订单不存在");
        }

        Map<String, Object> orderVo = new HashMap<String, Object>();
        orderVo.put("id", order.getId());
        orderVo.put("orderSn", order.getOrderSn());
        orderVo.put("addTime", order.getAddTime());
        orderVo.put("consignee", order.getConsignee());
        orderVo.put("mobile", order.getMobile());
        orderVo.put("address", order.getAddress());
        orderVo.put("goodsPrice", order.getGoodsPrice());
        orderVo.put("freightPrice", order.getFreightPrice());
        orderVo.put("actualPrice", order.getActualPrice());
        orderVo.put("orderStatusText", OrderUtil.orderMercStatusText(order));
        orderVo.put("handleOption", OrderUtil.build(order));
        orderVo.put("expCode", order.getShipChannel());
        orderVo.put("expNo", order.getShipSn());

        List<NearbyshopOrderGoods> orderGoodsList = orderGoodsService.queryByOid(order.getId());

        Map<String, Object> result = new HashMap<>();
        result.put("orderInfo", orderVo);
        result.put("orderGoods", orderGoodsList);

        // 订单状态为已发货且物流信息不为空
        //"YTO", "800669400640887922"
        if (order.getOrderStatus().equals(OrderUtil.STATUS_SHIP)) {
            ExpressInfo ei = expressService.getExpressInfo(order.getShipChannel(), order.getShipSn());
            result.put("expressInfo", ei);
        }

        return ResponseUtil.ok(result);

    }

    /**
     * 订单申请退款
     * <p>
     * 1. 检测当前订单是否能够退款；
     * 2. 设置订单申请退款状态。
     *
     * @param userId 用户ID
     * @param body   订单信息，{ orderId：xxx }
     * @return 订单退款操作结果
     */
    public Object refund(Integer userId, String body) {
        if (userId == null) {
            return ResponseUtil.unlogin();
        }
        Integer orderId = JacksonUtil.parseInteger(body, "orderId");
        if (orderId == null) {
            return ResponseUtil.badArgument();
        }

        NearbyshopOrder order = orderService.findById(orderId);
        if (order == null) {
            return ResponseUtil.badArgument();
        }
        if (!order.getUserId().equals(userId)) {
            return ResponseUtil.badArgumentValue();
        }

        OrderHandleOption handleOption = OrderUtil.build(order);
        if (!handleOption.isRefund()) {
            return ResponseUtil.fail(WxResponseCode.ORDER_INVALID_OPERATION, "订单不能取消");
        }

        // 设置订单申请退款状态
        order.setOrderStatus(OrderUtil.STATUS_REFUND);
        if (orderService.updateWithOptimisticLocker(order) == 0) {
            return ResponseUtil.updatedDateExpired();
        }

        //TODO 发送邮件和短信通知，这里采用异步发送
        // 有用户申请退款，邮件通知运营人员
        notifyService.notifyMail("退款申请", order.toString());

        return ResponseUtil.ok();
    }

    /**
     * 确认收货
     * <p>
     * 1. 检测当前订单是否能够确认收货；
     * 2. 设置订单确认收货状态。
     *
     * @param mercId 用户ID
     * @param body   订单信息，{ orderId：xxx }
     * @return 订单操作结果
     */
    @Transactional(rollbackFor = Exception.class)
    public Object confirm(Integer mercId, String body) {
        if (mercId == null) {
            return ResponseUtil.unlogin();
        }
        Integer orderId = JacksonUtil.parseInteger(body, "orderId");
        if (orderId == null) {
            return ResponseUtil.badArgument();
        }

        NearbyshopOrder order = orderService.findById(orderId);
        if (order == null) {
            return ResponseUtil.badArgument();
        }

        OrderHandleOption handleOption = OrderUtil.build(order);
        if (!handleOption.isConfirm()) {
            return ResponseUtil.fail(WxResponseCode.ORDER_INVALID_OPERATION, "订单不能确认收货");
        }

        Short comments = orderGoodsService.getComments(orderId);
        order.setComments(comments);

        order.setOrderStatus(OrderUtil.STATUS_CONFIRM);
        order.setConfirmTime(LocalDateTime.now());
        if (orderService.updateWithOptimisticLocker(order) == 0) {
            return ResponseUtil.updatedDateExpired();
        }

        //返利
        rebateService.addRebateData(order);
        return ResponseUtil.ok();
    }

    /**
     * 设置已提货
     * <p>
     * 1. 检测当前订单是否能够确认收货；
     * 2. 设置订单确认收货状态。
     *
     * @param mercId 用户ID
     * @param body   订单信息，{ orderIds：xxx }
     * @return 订单操作结果
     */
    public Object taken(Integer mercId, String body) {
        if (mercId == null) {
            return ResponseUtil.unlogin();
        }
        String orderIds = JacksonUtil.parseString(body, "orderIds");
        if (StringUtils.isEmpty(orderIds)) {
            return ResponseUtil.badArgument();
        }

        String[] ids = orderIds.split(",");

        for(String id:ids){
            NearbyshopOrder order = orderService.findById(Integer.valueOf(id));
            if (order == null) {
                return ResponseUtil.badArgument();
            }
            OrderHandleOption handleOption = OrderUtil.build(order);
            if (!handleOption.isTaken()) {
                return ResponseUtil.fail(WxResponseCode.ORDER_INVALID_OPERATION, "有订单不能设置已提货,订单编号:" + order.getOrderSn());
            }
            order.setOrderStatus(OrderUtil.STATUS_TAKEN);
            if (orderService.updateWithOptimisticLocker(order) == 0) {
                return ResponseUtil.updatedDateExpired();
            }
        }
        return ResponseUtil.ok();
    }

    /**
     * 发布到货提醒
     * <p>
     * 1. 检测当前订单是否能够确认收货；
     * 2. 设置订单确认收货状态。
     *
     * @param mercId 用户ID
     * @param body   订单信息，{ orderIds：xxx }
     * @return 订单操作结果
     */
    public Object sendAriveMsg(Integer mercId, String body) {
        if (mercId == null) {
            return ResponseUtil.unlogin();
        }
        String orderIds = JacksonUtil.parseString(body, "orderIds");
        if (StringUtils.isEmpty(orderIds)) {
            return ResponseUtil.badArgument();
        }

        String[] ids = orderIds.split(",");

        for(String id:ids){
            NearbyshopOrder order = orderService.findById(Integer.valueOf(id));
            if (order == null) {
                return ResponseUtil.badArgument();
            }
            OrderHandleOption handleOption = OrderUtil.build(order);
            if (!handleOption.isTaken()) {
                return ResponseUtil.fail(WxResponseCode.ORDER_INVALID_OPERATION, "有订单不能不能发送到货提醒,订单编号:" + order.getOrderSn());
            }

            NearbyshopMerc merc = mercService.queryByMercNo(order.getMercNo());
            NearbyshopUser user = userService.findById(order.getUserId());
            List<NearbyshopOrderGoods> goods = orderGoodsService.queryByOid(order.getId());
            String goodsNames = "";
            for(NearbyshopOrderGoods gd : goods){
                goodsNames += gd.getGoodsName() + " ";
            }
            // 请依据自己的模版消息配置更改参数
            String[] parms = new String[]{
                    merc.getMercAddr(),
                    merc.getMobile(),
                    merc.getMercName(),
                    order.getOrderSn(),
                    goodsNames,

            };

            notifyService.notifyWxTemplate(user.getWeixinOpenid(), NotifyType.ARRIVE, parms,
                    "pages/index/index?orderId=" + order.getId());
        }
        return ResponseUtil.ok();
    }

    /**
     * 商户查看订单列表
     *
     * @param mercNo   商户Id
     * @param showType 订单信息：
     *                 0，全部订单；
     *                 1，待付款；
     *                 2，待发货；
     *                 3，待收货；
     *                 4，待评价。
     * @param page     分页页数
     * @param size     分页大小
     * @return 订单列表
     */
    public Object list(String mercNo, Integer showType, Integer page, Integer size) {
        if (mercNo == null) {
            return ResponseUtil.unlogin();
        }

        List<Short> orderStatus = OrderUtil.orderStatus(showType);
        List<NearbyshopOrder> orderList = orderService.queryByOrderStatusMerc(mercNo, orderStatus, page, size);
        long count = PageInfo.of(orderList).getTotal();
        int totalPages = (int) Math.ceil((double) count / size);

        List<Map<String, Object>> orderVoList = new ArrayList<>(orderList.size());
        for (NearbyshopOrder order : orderList) {
            Map<String, Object> orderVo = new HashMap<>();
            orderVo.put("id", order.getId());
            orderVo.put("orderSn", order.getOrderSn());
            orderVo.put("actualPrice", order.getActualPrice());
            orderVo.put("orderStatusText", OrderUtil.orderMercStatusText(order));
            orderVo.put("handleOption", OrderUtil.build(order));
            orderVo.put("userNm", order.getConsignee());
            orderVo.put("userMobile", order.getMobile());

            NearbyshopGroupon groupon = grouponService.queryByOrderId(order.getId());
            if (groupon != null) {
                orderVo.put("isGroupin", true);
            } else {
                orderVo.put("isGroupin", false);
            }

            List<NearbyshopOrderGoods> orderGoodsList = orderGoodsService.queryByOid(order.getId());
            List<Map<String, Object>> orderGoodsVoList = new ArrayList<>(orderGoodsList.size());
            for (NearbyshopOrderGoods orderGoods : orderGoodsList) {
                Map<String, Object> orderGoodsVo = new HashMap<>();
                orderGoodsVo.put("id", orderGoods.getId());
                orderGoodsVo.put("goodsName", orderGoods.getGoodsName());
                orderGoodsVo.put("number", orderGoods.getNumber());
                orderGoodsVo.put("picUrl", orderGoods.getPicUrl());
                orderGoodsVoList.add(orderGoodsVo);
            }
            orderVo.put("goodsList", orderGoodsVoList);

            orderVoList.add(orderVo);
        }

        Map<String, Object> result = new HashMap<>();
        result.put("count", count);
        result.put("data", orderVoList);
        result.put("totalPages", totalPages);

        return ResponseUtil.ok(result);
    }

    /**
     * 商户查看订单列表
     *
     * @param mercNo   商户Id
     * @param startDt 起始日期：
     * @param endDt 结束日期：
     * @param page     分页页数
     * @param size     分页大小
     * @return 订单列表
     */
    public Object profitList(String mercNo, String startDt, String endDt, Integer page, Integer size) {
        if (mercNo == null) {
            return ResponseUtil.unlogin();
        }

        List<Short> orderStatus = new ArrayList<>();
        orderStatus.add(OrderUtil.STATUS_PAY);
        orderStatus.add(OrderUtil.STATUS_SHIP);
        orderStatus.add(OrderUtil.STATUS_CONFIRM);

        List<Map<String, String>> orderList = orderService.getProfitByGoods(mercNo, startDt, endDt, page, size);
        long count = PageInfo.of(orderList).getTotal();
        int totalPages = (int) Math.ceil((double) count / size);

        List<Map<String, Object>> orderVoList = new ArrayList<>(orderList.size());
        for (Map<String, String> order : orderList) {
            Map<String, Object> orderVo = new HashMap<>();
            orderVo.put("addDt", order.get("addDt"));
            orderVo.put("goodsName", order.get("goods_name"));
            orderVo.put("mercProfit", order.get("merc_profit"));
            orderVo.put("totalProfit", order.get("mercProfit"));
            orderVo.put("goodsNum", order.get("goodsNum"));

            orderVoList.add(orderVo);
        }

        Map<String, Object> result = new HashMap<>();
        result.put("sumProfit", orderService.getProfitByDt(mercNo, startDt, endDt));
        result.put("count", count);
        result.put("data", orderVoList);
        result.put("totalPages", totalPages);

        return ResponseUtil.ok(result);
    }

    /**
     * 商户查看订单列表
     *
     * @param mercNo   商户Id
     * @param startDt 起始日期：
     * @param endDt 结束日期：
     * @return 订单列表
     */
    public Object referProfitList(String mercNo, String startDt, String endDt) {
        if (mercNo == null) {
            return ResponseUtil.unlogin();
        }

        Map<String, Object> result = new HashMap<>();

        List<NearbyshopMerc> mercList = mercService.queryByReferMercNo(mercNo);

        result.put("count", mercList.size());

        if (mercList.size() == 0) {
            result.put("data", null);
            result.put("sumReferAmt", "0");
        }else {
            List<Map<String, String>> referMercVoList = new ArrayList<>();
            BigDecimal sumAmt = BigDecimal.ZERO;
            String referAmt;
            Map<String, String> referMercVo;
            for (NearbyshopMerc referMerc: mercList) {
                referAmt = orderService.getProfitByDt(referMerc.getMercNo(), startDt, endDt);
                BigDecimal referAmtReal = new BigDecimal(referAmt).multiply(SystemConfig.getMercReferPercent()).setScale(2, BigDecimal.ROUND_UP).stripTrailingZeros();
                sumAmt = referAmtReal.add(sumAmt);
                referMercVo = new HashMap<>();
                referMercVo.put("mercName", referMerc.getMercName());
                referMercVo.put("referAmt", referAmtReal.toPlainString());
                referMercVoList.add(referMercVo);
            }
            result.put("sumReferAmt", sumAmt.toPlainString());
            result.put("data", referMercVoList);
        }

        return ResponseUtil.ok(result);
    }


    public List<Map<String, Object>> listNew(List<NearbyshopOrder2> orderList) {
        List<Map<String, Object>> orderVoList = new ArrayList<>(orderList.size());
        for (NearbyshopOrder2 order : orderList) {
            Map<String, Object> orderVo = new HashMap<>();
            NearbyshopOrder od = new NearbyshopOrder();
            od.setOrderStatus(order.getOrderStatus().shortValue());
            orderVo.put("id", order.getId());
            orderVo.put("orderSn", order.getOrderSn());
            orderVo.put("actualPrice", order.getActualPrice());
            orderVo.put("orderStatusText", OrderUtil.orderMercStatusText(od));
            orderVo.put("handleOption", OrderUtil.build(od));
            orderVo.put("userNm", order.getConsignee());
            orderVo.put("userMobile", order.getMobile());
            orderVo.put("addTime",DateTimeUtil.getDateTimeNormalString(order.getAddTime()));
            NearbyshopGroupon groupon = grouponService.queryByOrderId(order.getId());
            if (groupon != null) {
                orderVo.put("isGroupin", true);
            } else {
                orderVo.put("isGroupin", false);
            }

            List<NearbyshopOrderGoods> orderGoodsList = orderGoodsService.queryByOid(order.getId());
            List<Map<String, Object>> orderGoodsVoList = new ArrayList<>(orderGoodsList.size());
            for (NearbyshopOrderGoods orderGoods : orderGoodsList) {
                Map<String, Object> orderGoodsVo = new HashMap<>();
                orderGoodsVo.put("id", orderGoods.getId());
                orderGoodsVo.put("goodsName", orderGoods.getGoodsName());
                orderGoodsVo.put("number", orderGoods.getNumber());
                orderGoodsVo.put("picUrl", orderGoods.getPicUrl());
                orderGoodsVoList.add(orderGoodsVo);
            }
            orderVo.put("goodsList", orderGoodsVoList);

            orderVoList.add(orderVo);
        }
        return orderVoList;
    }
}
